#include <string>
// A polynomial ring is a light weighted data structure
// contains only: variable names, characteristic
// R1 := Q[x,y] and R2 := Q[x,y] are viewed to be different
// however, polynomials in R1 and R2 are compatible

// limitations: degree of a polynomial is limited by size_t
// variables equals to the indices: Q[x,y,z] == 0=x, 1=y, 2=z 


//! the types to wrap
enum PolynomialType { COCOA, BPAS, SDMP };

class Polynomial {
		public:

				/* constructors */

				//! zero constructor
				Polynomial(const PolynomialRing& R, PolynomialType flag=COCOA);

				//! raw data constructor
				Polynomial(const PolynomialRing& R, void * data, PolynomialType flag=COCOA); //try dynamic_cast conversion

				//! copy constructor
				Polynomial(const Polynomial& );


				//! destructor
				~Polynomial();


				/* arithmetic operations: operators  */

				//! addition
				Polynomial operator+(const Polynomial& ) const;
				
				//! subtraction
				Polynomial operator-(const Polynomial& ) const;

				//! multiplication
				Polynomial operator*(const Polynomial& ) const;


				//! exact division
				Polynomial operator/(const Polynomial& ) const;

				//! negation
				Polynomial operator-() const;


				/*  other operations  */
				/* basically, all this operations can be implemented 
				 * by the basic operations, however, dedicated
				 * algorithms are required for efficiency considerations */

				//! addition: long form, save the result in the third polynomial
				const Polynomial& add(const Polynomial& , Polynomial& ) const;

				//! subtraction: long form
				const Polynomial& minus(const Polynomial& ,  Polynomial& ) const;
	
				//! multiplication: long form
				const Polynomial& times(const Polynomial& ,  Polynomial& ) const;
	
				//! exact division: long form
				const Polynomial& divide(const Polynomial& , Polynomial& ) const;

				//! take exponential
				Polynomial exponential(const size_t e) const;
				const Polynomial& exponential(const size_t e,  Polynomial& ) const;

	
				//! partial degree w.r.t. v
				size_t degree(const size_t v);

				//! total degree 
				size_t degree();

				//! substitute a variable with another polynomial/value
				Polynomial evaluate (const size_t x, const Polynomial &val) const;

				/* Syntax sugar: for Maple compatibility */

				//! initial w.r.t. variable v
				friend Polynomial lcoeff(const Polynomial &, const size_t v);
				
				//! partial degree w.r.t. v
				friend size_t degree(const Polynomial &, const size_t v);

				//! total degree 
				friend size_t degree(const Polynomial &);

				//! coeff of v^d 
				friend Polynomial coeff(const Polynomial &, const size_t v, const size_t d);
				friend vector<Polynomial> coeffs(const Polynomial &, const size_t v);
				
	
		private:
				//! a flag indicating implementation details of 
				// the polynomial data
				PolynomialType __flag; // indicating implementation detail
				
				//! the actually data for a polynomial
				void* __data;  // the true data, has to be retrived by type indicator

				//! convert data from different representations
				const Polynomial& __convert(PolynomialType flag);

				//! the polynomial ring
				// check if two polynomial are compatible for certain operations
				// ring has to be alive ...  howto ensure this?
				// universal symbolic ring: ring contains all variables in the 
				// running environment
				PolynomialRing& __ring;

};


